using System;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace pginstaller.tasks
{
    public class zip : Task
    {
        /* List of files to include in the ZIP */
        [Required]
        public string[] Sources
        {
            get { return _sources; }
            set { _sources = value; }
        }

        /* Destination ZIP file name, including extension and path */
        [Required]
        public string Destination
        {
            get { return _destination; }
            set { _destination = value; }
        }

        /*
         * Base directory. Source files are derived as SourceBase\Sources, but 
         * in the ZIP they are stored without the SourceBase part
         */
        public string SourceBase
        {
            get { return _sourcebase; }
            set { _sourcebase = value; }
        }

        /*
         * Set to true to ignore *all* directories
         */
        public bool FlattenDirectories
        {
            get { return _flattendirectories; }
            set { _flattendirectories = value; }
        }
        
        /*
         * Strip prefix. Specifies a prefix to remove from files when
         * added to the archive. For example, with a StripPrefix =
         * "C:\pgBuild\pginst-8.3", the file "C:\pgBuild\pginst-8.3\pgsql\configure"
         * will be archived as "\pgsql\configure"
         * This parameter is ignored if FlattenDirectories=true
         */
        public string StripPrefix
        {
            get { return _stripprefix; }
            set { _stripprefix = value; }
        }

        /* Internal storage for the properties */
        private string[] _sources;
        private string _destination;
        private string _sourcebase = "";
        private string _stripprefix = "";
        private bool _flattendirectories = false;
        
        /* Perform the actual build task */
        public override bool Execute()
        {
            sbyte[] buf = new sbyte[8192];
            int len;
            long insize = 0;
            FileInfo fiOut;

            Log.LogMessage("Creating ZIP archive {0}", _destination);

            if (_sourcebase != "" && !_sourcebase.EndsWith("\\"))
                _sourcebase += "\\";

            /*
             * Verify if files exist before we start to build the ZIP, to lessen the risk of
             * leaving an empty ZIP file around.
             */
            foreach (string srcfile in _sources)
            {
                if (!File.Exists(_sourcebase + srcfile))
                {
                    Log.LogError("File {0} does not exist.", _sourcebase + srcfile);
                    return false;
                }
            }

            /*
             * The J# runtime actually contains native ZIP code. So use that, since the J# runtime
             * is a standard part of the .net framework these days, unlike things like SharpZipLib
             */
            java.io.FileOutputStream zout = new java.io.FileOutputStream(_destination);
            java.util.zip.ZipOutputStream zouts = new java.util.zip.ZipOutputStream(zout);
            foreach (string srcfile in _sources)
            {
                FileInfo fi = new FileInfo(_sourcebase + srcfile);
                /*
                 * Generate the target name for the file in the archive.
                 * FlattenDirectories will bung everything in the root, otherwise,
                 * if StripPrefix is set, it will be removed from any matching
                 * filenames before they are added to the archive.
                 */
                string targetfile;
                if (_flattendirectories == true)
                {
                    targetfile = Path.GetFileName(srcfile);
                }
                else
                {
                    if (srcfile.Substring(0, _stripprefix.Length) == _stripprefix)
                    {
                        targetfile = srcfile.Substring(_stripprefix.Length).Replace('\\', '/');
                    }
                    else
                    {
                        targetfile = srcfile.Replace('\\', '/');
                    }
                }

                Log.LogMessage("Compressing file {0} as {1}, {2} bytes...", srcfile, targetfile, fi.Length);
                insize += fi.Length;
                java.io.FileInputStream fin = new java.io.FileInputStream(_sourcebase + srcfile);
                java.util.zip.ZipEntry zentry = new java.util.zip.ZipEntry(targetfile);
                zouts.putNextEntry(zentry);
                while ((len = fin.read(buf)) >= 0)
                {
                    zouts.write(buf, 0, len);
                }
                zouts.closeEntry();
                fin.close();
            }
            zouts.close();
            zout.close();

            fiOut = new FileInfo(_destination);
            Log.LogMessage("Done, compression rate {0}%", fiOut.Length * 100 / insize);
            return true;
        }
    }
}